var gamejs = require("gamejs");
var Sprite = require("gamejs/sprite").Sprite;
var radarModule = require("js/radar");
var enemyModule = require("js/enemy");
var obstacleModule = require("js/obstacle");
var turretModule = require("js/turret");
var PauseScene = require("js/scenes/pauseScene").PauseScene;
var LevelClearedScene = require("js/scenes/levelClearedScene").LevelClearedScene;
var GameOverScene = require("js/scenes/gameOverScene").GameOverScene;
var NightStoreScene = require("js/scenes/nightStoreScene").NightStoreScene;

/**
 * NightScene, the scene where our planet will be attacked by a wave of enemies.
 */

/**
 * Creates a NightScene object, in this scene enemies will attack the planet.
 *
 * @param {ScenesDirector} director The ScenesDirector object that directs the game.
 * @param {Match} match The Match object representing the game.
 * @param {Array} wave An Array of object in which each object has an enemy property that
 * stores an Enemy object and a time property that stores the time when show the enemy.
 * The time property of successive elements must be in non decreasing order.
 *
 * @constructor
 */
var NightScene = exports.NightScene = function(director, match, wave) {
	this.director = director;
	this.match = match;
	this.selectedItems = null;
	this.nightAction = null;
	this.timer = 0;// to create the wave
	this.nightActionTimer = 0;// to buy another NightAction
	this.wave = wave;
	this.font = new gamejs.font.Font("15px Verdana");
	
	// HUD background image
	this.hudBackground = gamejs.image.load(IMAGE_ROOT + "hudBackground.png");
	this.hudBackground.setAlpha(0.5);
	// pause image
	this.pause = new Sprite();
	this.pause.image = gamejs.image.load(IMAGE_ROOT + "pause.png");
	this.pause.rect = new gamejs.Rect([5, 5], this.pause.image.getSize());
	
	// buy image
	this.disabledBuy = gamejs.image.load(IMAGE_ROOT + "disabledBuild.png");
	this.enabledBuy = gamejs.image.load(IMAGE_ROOT + "build.png");
	this.canBuy = true;
	this.buy = new Sprite();
	this.buy.image = this.enabledBuy;
	this.buy.rect = new gamejs.Rect([10, 160], this.buy.image.getSize());
	
	// sell image
	this.disabledSell = gamejs.image.load(IMAGE_ROOT + "disabledSell.png");
	this.enabledSell = gamejs.image.load(IMAGE_ROOT + "sell.png");
	this.canSell = false;
	this.sell = new Sprite();
	this.sell.image = this.disabledSell;
	this.sell.rect = new gamejs.Rect([10, 240], this.sell.image.getSize());
	
	// upgrade image
	this.disabledUpgrade = gamejs.image.load(IMAGE_ROOT + "disabledUpgrade.png");
	this.enabledUpgrade = gamejs.image.load(IMAGE_ROOT + "upgrade.png");
	this.canUpgrade = false;
	this.upgrade = new Sprite();
	this.upgrade.image = this.disabledUpgrade;
	this.upgrade.rect = new gamejs.Rect([10, 320], this.upgrade.image.getSize());
	
	// info image
	this.disabledShowInfo = gamejs.image.load(IMAGE_ROOT + "disabledShowInfo.png");
	this.enabledShowInfo = gamejs.image.load(IMAGE_ROOT + "showInfo.png");
	this.canInfo = false;
	this.showInfo = new Sprite();
	this.showInfo.image = this.disabledShowInfo;
	this.showInfo.rect = new gamejs.Rect([10, 400], this.showInfo.image.getSize());
	
	// play night theme via director
	this.director.getSoundManager().stop();
	this.director.getSoundManager().playNight();
};

/**
 * Takes a NightAction so the player can execute it in a point of the map.
 *
 * @param {NightAction} action The NightAction to execute.
 */
NightScene.prototype.setNightAction = function(action) {
	if( ! this.match.hasCredit(action.getBuyPrice())) {
		throw new Error("Not enough credit.");
	}
	// cannot buy another NightAction
	this.canBuy = false;
	
	// once added the NightAction must be executed, pay it
	this.match.changeCredit( - action.getBuyPrice());
	this.nightAction = action;
	
	// show the action symbol at the map center
	var centerX = Math.round(WIDTH_OFFSET + MAP_WIDTH / 2);
	var centerY = Math.round(HEIGHT_OFFSET + MAP_HEIGHT / 2);
	this.nightAction.setPosition([centerX, centerY]);
};

/**
 * Updates the scene and its contents, adding the given enemies
 * to the map and managing the various events.
 *
 * @param {number} msDuration The time past from the last call, in ms.
 */
NightScene.prototype.update = function(msDuration) {
	// update the match and everything else in cascade
	this.match.update(msDuration);
	
	if (this.nightAction) {
		// update the nightAction
		this.nightAction.update(msDuration);
		
		// check if action is over
		if (this.nightAction.isOver()) {
			// if the nightAction is over set the reload time
			this.nightActionTimer = this.nightAction.getReloadTime();
			// and remove the nightAction
			this.nightAction = null;
		}
	}
	else {
		// update the nightActionTimer
		this.nightActionTimer -= msDuration;
		
		if(this.nightActionTimer <= 0) {
			// if time is over enable the buy button
			this.canBuy = true;
		}
	}
	
	// add enemies according the given wave
	this.timer += msDuration;
	while(this.wave.length > 0 && this.wave[0].time <= this.timer) {
		this.match.getMap().addGameEntity(this.wave.shift().enemy);
	}
	
	var events = gamejs.event.get();
	
	for ( i = 0; i < events.length; i++) {
		var currentEvent = events[i];
		
		if(this.nightAction) {
			// there's an action to execute
			var dim = this.nightAction.image.getSize();
			if(currentEvent.type == gamejs.event.MOUSE_MOTION) {
				// move the pictures
				var newPos = currentEvent.pos;

				// avoid invalid positions
				if(newPos[X] < WIDTH_OFFSET) {
					newPos[X] = WIDTH_OFFSET;
				}
				if(newPos[X] >= (WIDTH_OFFSET + MAP_WIDTH)) {
					newPos[X] = (WIDTH_OFFSET + MAP_WIDTH) - 1;
				}
				if(newPos[Y] < HEIGHT_OFFSET) {
					newPos[Y] = HEIGHT_OFFSET;
				}
				if(newPos[Y] >= (HEIGHT_OFFSET + MAP_HEIGHT)) {
					newPos[Y] = (HEIGHT_OFFSET + MAP_HEIGHT) - 1;
				}

				// point where to show the action
				var actualPoint = [newPos[X] - WIDTH_OFFSET, newPos[Y] - HEIGHT_OFFSET];
				this.nightAction.setPosition(actualPoint);

				// go to the next event
				continue;
			}

			if(currentEvent.type == gamejs.event.MOUSE_UP) {
				// execute the action
				var point = currentEvent.pos;

				// manage boundary click
				if(point[X] < (WIDTH_OFFSET + dim[WIDTH] / 2)) {
					point[X] = (WIDTH_OFFSET + dim[WIDTH] / 2);
				}
				if(point[X] >= (WIDTH_OFFSET + MAP_WIDTH - dim[WIDTH] / 2)) {
					point[X] = (WIDTH_OFFSET + MAP_WIDTH - dim[WIDTH] / 2) - 1;
				}
				if(point[Y] < (HEIGHT_OFFSET + dim[HEIGHT] / 2)) {
					point[Y] = (HEIGHT_OFFSET + dim[HEIGHT] / 2);
				}
				if(point[Y] >= (HEIGHT_OFFSET + MAP_HEIGHT - dim[HEIGHT] / 2)) {
					point[Y] = (HEIGHT_OFFSET + MAP_HEIGHT - dim[HEIGHT] / 2) - 1;
				}
				
				// point where to execute the action
				var actualPoint = [point[X] - WIDTH_OFFSET, point[Y] - HEIGHT_OFFSET];
				this.nightAction.setPosition(actualPoint);
				
				// executes the nightAction
				this.nightAction.execute();
				
				// the action is executed, don't process other events
				break;
			}
		}
		
		// process only one valid mouse click(this is the cause of the break statements)
		if (currentEvent.type == gamejs.event.MOUSE_UP) {
			var point = currentEvent.pos;
			if (this.pause.rect.collidePoint(point)) {
				// click on pause
				this.selectedItem = null;
				this.director.pushScene(new PauseScene(this.director, this.match));
				// no break statement because selectedItem is changed,
				// need to update
			}
			
			if (this.canBuy && this.buy.rect.collidePoint(point)) {
				// click on buy
				this.selectedItem = null;
				this.director.pushScene(new NightStoreScene(this.director, this.match));
				// no break statement because selectedItem is changed,
				// need to update
			}

			// there's an object selected
			if (this.selectedItem != null) {
				if (this.canSell && this.sell.rect.collidePoint(point)) {
					// click on sell
					this.selectedItem.sell();
					this.selectedItem = null;// item sold
					// no break statement because selectedItem is changed,
					// need to update
				}
				
				if (this.canUpgrade && this.upgrade.rect.collidePoint(point)) {
					// click on upgrade
					this.selectedItem.upgrade();
					// no break statement because selectedItem is changed,
					// need to update
				}
				
				if (this.canShowInfo && this.showInfo.rect.collidePoint(point)) {
					// click on show info
					this.director.pushScene(new InfoScene(this.director, this.selectedItem));
					break;
				}
			}

			// checks if the click is on an item of the map
			var mapPoint = [point[X] - WIDTH_OFFSET, point[Y] - HEIGHT_OFFSET];
			var selRadars = this.match.getMap().getRadars().collidePoint(mapPoint);
			var selTurrets = this.match.getMap().getTurrets().collidePoint(mapPoint);
			var selObstacles = this.match.getMap().getObstacles().collidePoint(mapPoint);
			var numSelected = selRadars.length + selTurrets.length + selObstacles.length;
			if(numSelected == 1) {
				// there is just one item selected
				if(selRadars[0]) {
					this.selectedItem = selRadars[0];
				}
				
				if(selTurrets[0]) {
					this.selectedItem = selTurrets[0];
				}
				
				if(selObstacles[0]) {
					this.selectedItem = selObstacles[0];
				}
			}
			else {
				// there are zero or more than one items selected
				this.selectedItem = null;// selection not valid
			}
			
			// if an item is selected, checks if it can be upgraded, sold
			// or has some info to show
			if(this.selectedItem != null) {
				if(this.selectedItem.upgrade && this.selectedItem.canUpgrade()) {
					this.canUpgrade = true;
				}
				else {
					this.canUpgrade = false;
				}
				
				if(this.selectedItem.sell && this.selectedItem.isSaleable()) {
					this.canSell = true;
				}
				else {
					this.canSell = false;
				}
				
				if(this.selectedItem.getInfo) {
					this.canShowInfo = true;
				}
				else {
					this.canShowInfo = false;
				}
			}
			else {
				// nothing selected
				this.canUpgrade = false;
				this.canSell = false;
				this.canShowInfo = false;
			}
		}
		else if (currentEvent.type == gamejs.event.MOUSE_MOTION) {
			var point = currentEvent.pos;
			if (this.pause.rect.collidePoint(point))
				document.getElementsByTagName("body")[0].style.cursor ='pointer';
			else if (this.canBuy && this.buy.rect.collidePoint(point))
				document.getElementsByTagName("body")[0].style.cursor ='pointer';
			else
				document.getElementsByTagName("body")[0].style.cursor ='default';
		}
	}
	
	// sets the correct image(enabled/disabled)
	this.buy.image = this.canBuy ? this.enabledBuy : this.disabledBuy;
	this.sell.image = this.canSell ? this.enabledSell : this.disabledSell;
	this.upgrade.image = this.canUpgrade ? this.enabledUpgrade : this.disabledUpgrade;
	this.showInfo.image = this.canShowInfo ? this.enabledShowInfo : this.disabledShowInfo;
	
	var allEnemies = this.match.getMap().getEnemies().sprites();
	
	// if enemies decreased the health of the planet to 0 (isInGame = false) than game over
	if( ! this.match.isInGame()){
		this.director.changeScene(new GameOverScene(this.director));
		return;
	}

	if((allEnemies.length == 0) && (this.wave.length == 0)) {
		// no more enemies, go to next level
		this.director.changeScene(new LevelClearedScene(this.director, this.match));
		// check if the next level is a new mission
		if(this.match.isLastLevel()) {
			this.match.nextMission();
		}
		else {
			// else go to the next level
			this.match.nextLevel();
		}
	}
};

/**
 * Changes to night the map background
 */
NightScene.prototype.setActive = function() {
	this.match.getMap().setNight();
};


/**
 * Draws the scene and its contents in the director Surface.
 */
NightScene.prototype.draw = function() {
	var surface = this.director.getSurface();
	surface.blit(this.hudBackground);
	this.pause.draw(surface);
	this.buy.draw(surface);
	this.sell.draw(surface);
	this.upgrade.draw(surface);
	this.showInfo.draw(surface);
	// draws the match(health, credits and score) and everything else
	// (the map) in cascade
	this.match.draw(surface);
	
	// writes the past time
	surface.blit(this.font.render((this.timer / 1000).toFixed(1), "#FF0000"), [20, 70]);
	
	if (this.nightAction != null) {
		this.nightAction.draw(surface);
	}

	if (this.selectedItem != null) {
		if (this.canSell) {
			// writes the credits received if the selected item will sold
			surface.blit(this.font.render(this.selectedItem.getResaleValue(), "#82E0FF"), [35, 290]);
		}
		
		if ( ! this.selectedItem.isAtMaximumLevel()) {
			// writes the credits needed to upgrade the selected item
			surface.blit(this.font.render(this.selectedItem.getUpgradePrice(), "#82E0FF"), [35, 370]);
		}
	}
};